package MemoryManager;

import java.util.ArrayList;
import java.util.LinkedList;

public class Memory {
    // 就绪队列
    LinkedList<Integer> readyQueue = new LinkedList<>();
    // 阻塞队列
    LinkedList<Integer> blockedQueue = new LinkedList<>();
    // 进程列表
    LinkedList<Process> processes = new LinkedList<>();
    // 空闲块链表
    LinkedList<EmptyBlock> emptyBlocks = new LinkedList<>();
    // 内存块链表
    LinkedList<MemoryBlock> blocks = new LinkedList<>();
    // 设备列表
    LinkedList<Device> devices = new LinkedList<>();
    // PCB列表
    LinkedList<PCB> pcbs = new LinkedList<>();
    // 内存模拟
    ArrayList<String> commands;
    // 最大内存
    public static int MAX_MEMORY = 100;
    // 就绪队列上限
    public static int MAX_AMOUNT_OF_PID_IN_QUEUE = 5;
    // 资源最大值
    public static int MAX_SOURCE_NUM = 7;
    // 资源余量
    public int A;
    public int B;
    public int C;
    // 运行态PID
    public int runningPID = -1;
    // 下一PID
    public int nextPID;
    // 系统时间
    public int time;

    public LinkedList<MemoryBlock> getBlocks() {
        return blocks;
    }

    public LinkedList<Integer> getReadyQueue() {
        return readyQueue;
    }

    public LinkedList<Integer> getBlockedQueue() {
        return blockedQueue;
    }

    public Memory() {
        // 初始化内存
        blocks.add(new MemoryBlock(0, 20));
        blocks.add(new MemoryBlock(MAX_MEMORY - 20));
        commands = new ArrayList<>(MAX_MEMORY);
        // 初始化PID
        nextPID = 1;
        // 初始化系统时间
        time = 0;
        // 初始化设备
        devices.add(new Device("A", 3));
        devices.add(new Device("B", 3));
        devices.add(new Device("C", 3));
        // 初始化资源余量
        A = MAX_SOURCE_NUM;
        B = MAX_SOURCE_NUM;
        C = MAX_SOURCE_NUM;
    }

    // 根据文件内容添加进程
    public void addProcess(String fileContent) {
        Process newProcess = new Process(fileContent, nextPID, time);
        // 分配内存
        int startLoc = distribution(newProcess);
        if (startLoc == -1) {
            System.out.println("Out of memory.");
            return;
        }
        // 检查资源是否有可能满足需求
        if (newProcess.A > MAX_SOURCE_NUM || newProcess.B > MAX_SOURCE_NUM || newProcess.C > MAX_SOURCE_NUM) {
            System.out.println("Can not satisfy the resources demand.");
            return;
        }
        // 分配资源
        if (moveProcesstoReadyQueue(newProcess) != -1) {
            processes.add(newProcess);
            pcbs.add(new PCB(newProcess.getPID(), startLoc, newProcess.getCommands().length));
            ++nextPID;
        }
    }

    // 内存空间分配，若分配成功返回起址，否则返回-1
    public int distribution(Process process) {
        // 最先适应算法
        int p = 0;
        while (p < blocks.size()) {
            if (blocks.get(p).isAvailable() && blocks.get(p).getAllocated() > process.getCommands().length) {
                int sizeLeft = blocks.get(p).getAllocated() - process.getCommands().length;
                if (sizeLeft == 0) {
                    // 无剩余空间
                    blocks.add(p, new MemoryBlock(process.getPID(), process.getCommands().length));
                    blocks.remove(p + 1);
                } else {
                    // 有剩余空间
                    blocks.add(p, new MemoryBlock(sizeLeft));
                    blocks.add(p, new MemoryBlock(process.getPID(), process.getCommands().length));
                    blocks.remove(p + 2);
                }
                return p;
            }
            ++p;
        }
        return -1;
    }

    // 按PID查找进程，若找不到返回null
    public Process findProcessbyPID(int PID) {
        for (Process process : processes) {
            if (process.getPID() == PID)
                return process;
        }
        return null;
    }

    // 按PID查找PCB，若找不到返回null
    public PCB findPCBbyPID(int PID) {
        for (PCB pcb : pcbs) {
            if (pcb.getPID() == PID)
                return pcb;
        }
        return null;
    }

    // 读取下一条指令
    public String readCommand(int PID) {
        PCB pcb = findPCBbyPID(PID);
        return commands.get(pcb.startLoc + pcb.processCounter++);
    }

    // 运行
    public void run() {
        // 重置进程【是否已操作】的状态
        for (Process process : processes) {
            process.runned = false;
        }
        // CPU运行
        if (runningPID == -1) {
            // CPU空闲时请求调度
            runningPID = processScheduling();
            // timeSlice = TIME_SLICE_NUM;
        } else {
            int temp = runningPID;
            runningPID = findProcessbyPID(runningPID).run();
            if (runningPID == -1) {
                // 上一个进程完成
                runningPID = processScheduling();
            }
            if (findProcessbyPID(temp).getProcessState() == Process.STATE_FINISH) {
                // 回收资源
                recycle(temp);
                removeProcessbyPID(temp);
            }
            else if (findProcessbyPID(temp).getProcessState() == Process.STATE_BLOCK) {
                // 回收资源
                recycle(temp);
                blockedQueue.offer(temp);
            }
        }
        // 阻塞队列运行
        for (int i = 0; i < blockedQueue.size(); ++i) {
            Process process = findProcessbyPID(blockedQueue.get(i));
            if (process.getProcessState() == Process.STATE_READY) {
                if (moveProcesstoReadyQueue(process) != -1) {
                    blockedQueue.remove(i--);
                }
            } else if (process.getProcessState() == Process.STATE_BLOCK) {
                process.run();
            }
        }
        // 单位时间流动
        ++time;
    }

    // 移动进程至就绪队列
    // 若添加失败返回-1，否则返回PID
    public int moveProcesstoReadyQueue(Process process) {
        // 通过资源余量限制
        if (process.A > A || process.B > B || process.C > C)
            return -1;
        // 通过最大就绪队列长度限制
        if (readyQueue.size() == MAX_AMOUNT_OF_PID_IN_QUEUE)
            return -1;
        // 资源分配
        A -= process.A;
        B -= process.B;
        C -= process.C;
        // 添加至就绪队列
        readyQueue.add(process.getPID());
        return process.getPID();
    }

    // 回收资源
    public void recycle(int PID) {
        Process process = findProcessbyPID(PID);
        A += process.A;
        B += process.B;
        C += process.C;
    }
    
    // 进程调度
    public int processScheduling() {
        // FCFS
        Integer res = readyQueue.poll();
        if (res == null)
            return -1;
        return Integer.valueOf(res);
    }

    // 根据PID移除进程
    public void removeProcessbyPID(int PID) {
        for (int i = 0; i < processes.size(); ++i)
        {
            if (processes.get(i).getPID() == PID)
                processes.remove(i);
        }
        for (int i = 0; i < blocks.size(); ++i)
        {
            if (!blocks.get(i).isAvailable() && blocks.get(i).getPid() == PID) {
                blocks.get(i).setAvailable(true);
                if (i < blocks.size() - 1 && blocks.get(i + 1).isAvailable()) {
                    // 如果下一块是空闲块，合并这一块和下一块
                    blocks.get(i).setAllocated(blocks.get(i).getAllocated() + blocks.get(i + 1).getAllocated());
                    blocks.remove(i + 1);
                }
                if (i > 0 && blocks.get(i - 1).isAvailable()) {
                    // 如果上一块是空闲块，合并上一块和这一块
                    blocks.get(i - 1).setAllocated(blocks.get(i).getAllocated() + blocks.get(i - 1).getAllocated());
                    blocks.remove(i);
                }
                break;
            }
        }
    }

    // 打印信息
    public void SS() {
        System.out.println("time: " + time);
        System.out.print("running: " + runningPID);
        if (runningPID != -1) {
            System.out.println(" " + findProcessbyPID(runningPID).A + " " + findProcessbyPID(runningPID).B + " "
                    + findProcessbyPID(runningPID).C);
        } else {
            System.out.println();
        }
        // 输出就绪队列
        System.out.println("ReadyQueue:");
        for (int p : readyQueue)
            System.out.println(p + " " + findProcessbyPID(p).A + " " + findProcessbyPID(p).B + " " + findProcessbyPID(p).C);
        for (int i = 0; i < MAX_AMOUNT_OF_PID_IN_QUEUE - readyQueue.size(); ++i)
            System.out.println();
        // 输出阻塞队列
        System.out.println("BlockQueue:");
        for (int p : blockedQueue)
            System.out.println(p + " " + findProcessbyPID(p).A + " " + findProcessbyPID(p).B + " " + findProcessbyPID(p).C);
        for (int i = 0; i < MAX_AMOUNT_OF_PID_IN_QUEUE - blockedQueue.size(); ++i)
            System.out.println();
        // 输出资源余量
        System.out.println("Resource left:");
        System.out.println(String.format("A: %d  B: %d  C: %d", A, B, C));
        // 输出内存使用情况
        System.out.println("Memory usage:");
        for (MemoryBlock memoryBlock : blocks) {
            memoryBlock.printInfo();
        }
        System.out.println();
    }

    public LinkedList<Process> getAllProcess() {
        return processes;
    }

    public int getTime() {
        return time;
    }

    public void setTime(int time) {
        this.time = time;
    }

}
